home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr53 / 124_01.zip / TEX.C < prev    next >
Text File  |  1993-06-01  |  37KB  |  1,196 lines

  1. /*
  2.  * small tex - (tau epsilon chi; pronounced `tech') A stripped down
  3.  *    version of Knuths TEX for micros. This uses the Fancy Font/Epson
  4.  *    system as an output device.
  5.  *
  6.  *    copyright (c) 1982, Mike Meyer
  7.  *
  8.  *    This code and all documentation that accompanies it may be freely
  9.  *    distributed by anyone who has a copy, under the following provisions:
  10.  *
  11.  *        1) My copyright notices stay where they are.
  12.  *        2) The source is distributed with the package.
  13.  *        3) Fixes and improvements eventually come back to me.
  14.  *
  15.  *    Note that this does not disallow you from selling copies of this program *    to other people, so long as my copyright notices, etc stay with it. I
  16.  *    don't care if you make money of my work - I just want credit for having
  17.  *    done it.
  18.  *
  19.  *        <mike
  20.  */
  21. #define VERSION        1
  22. #define RELEASE        'a'
  23. #define    DATE        "December 1982"
  24.  
  25. #include <a:bdscio.h>
  26.  
  27. /*
  28.  * DEBUG - until such a time as I can make the #)(%&*#% thing justify
  29.  *    with my margins, the code to let users play with the right margin
  30.  *    is turned off. This hardwired to RIGHTDEFAULT.
  31.  */
  32. #define    RIGHT        FALSE
  33.  
  34. /*
  35.  * TRACEPOINTS - turns on some printfs to trace the margins/breakpoints.
  36.  */
  37. #define    TRACEPOINTS    FALSE
  38.  
  39. /* Some characters that are magic to tex */
  40. #define SPACE        ' '
  41. #define NL        '\012'
  42. #define CR        '\015'
  43. #define TAB        '\t'
  44. #define OPEN        '{'
  45. #define CLOSE        '}'
  46. #define COMMAND        '\\'
  47. #define MATHMODE    '$'
  48. #define    COMMENT        '%'
  49. #define    PARAMETER    '#'
  50. #define FONTCHAR    ':'
  51.  
  52. /* the names  of some strings we print every once and a while (ff commands) */
  53. #define ENDLINE        "\n"
  54. #define JUSTON        "\\j"
  55. #define JUSTOFF        "\\k"
  56. #define CENTER        "\\c"
  57. #define    LEFTIFY        "\\b"
  58. #define RIGHTIFY    "\\r"
  59. #define UNDERLINE    "\\u"
  60. #define ONMATH        ""
  61. #define OFFMATH        ""
  62. #define ONDISPLAY    "\\b\n\n\\c"
  63. #define OFFDISPLAY    "\n\n"
  64. #define PARAGRAPH    "\\b\n\n"
  65. #define    PAGE        "\\b\n\\p"
  66.  
  67. /* And some formats for ff commands for us to use */
  68. #define FORMLINE    "\\a%04d"
  69. #define FORMCHAR    "\\d%03d"
  70. #define FORMHORIZ    "\\i%03d"
  71. #define    FORMVERT    "\\b\n\\v%04d"
  72. #define FORMFONT    "\\f%d"
  73.  
  74. /* and now some magic constants */
  75. #define LEFTDEFAULT    0
  76. #define RIGHTDEFAULT    900
  77. #define INDENTDEFAULT    0
  78. #define DEFAULTCHARLEN    12
  79. #define    FILENAMELEN    15
  80. #define COMLENGTH    10
  81. #define    NUMFONTS    10
  82. #define NUMDEFS        200
  83. #define ASCIICHARS    128
  84. #define INBUF        1024
  85.  
  86. /* and a (the?) magic file name... */
  87. #define FFIFILE        "tex.ffi"
  88.  
  89. /* and a few macros */
  90. #define strequal(x,y)    (!strcmp(x, y))
  91.  
  92. /* and a few global variables */
  93. char    mathmode, displaymode ;        /* a pair of mathmode flags */
  94. char    fillmode  ;            /* are we doing word filling? */
  95. char    FFflag ;            /* Fancy Font commands valid? */
  96. char    mathfont, textfont ;        /* the math and text mode fonts */
  97. char    *endpointer, inbuffer[INBUF] ;/* input buffer & pointer into it */
  98. int    inchar ;            /* the next char to be processed */
  99. int    rightmargin, leftmargin ;    /* current margin settings */
  100. int    indent ;            /* indent for paragraphs */
  101. int    currentpoint, lastbreakpoint ;    /* last word break & current char */
  102. char    outfile[BUFSIZ] ;        /* output file pointer */
  103. int    nextdef ;            /* the next free defdata structure */
  104. int    cfont ;                /* the current font number */
  105. char    passtoff[INBUF] ;        /* user commands to fancy fonts */
  106. char    fontnames[NUMFONTS][FILENAMELEN] ;
  107.                     /* the names of the font files used */
  108. struct    chardata {
  109.     char    f_width ;        /* how much space it takes */
  110.     char    f_leftmargin ;        /* how much left margin it has */
  111.     char    f_inkwidth ;        /* the width of ink in the character */
  112.     char    f_inkheight ;        /* how high the ink is */
  113.     char    f_inkbottom ;        /* how low the ink goes */
  114.     } fontdata[NUMFONTS][ASCIICHARS] ;
  115.  
  116. struct    defdata {
  117.     char    d_defname[COMLENGTH] ;/* the define name */
  118.     char    *d_parameter ;        /* does it have a parameter */
  119.     char    *d_altparm ;        /* a pointer into parameter */
  120.     char    *d_replace ;        /* the replacement string */
  121.     char    *d_nextchar ;        /* a pointer into it */
  122.     } defines[NUMDEFS] ;
  123.  
  124. /* and a few non-int global functions */
  125. char    width() ;
  126. int    readch(), defch(), stringch() ;
  127.  
  128. /* these are a few (crock, a LOT) of my favorite (?) things */
  129.  
  130. main(argc, argv) char **argv; {
  131.     char    *p, filename[FILENAMELEN], infile[BUFSIZ] ;
  132.  
  133.     printf("tex version %d%c - %s\n", VERSION, RELEASE, DATE) ;
  134.     puts("For use with pfont version 1\n") ;
  135.     puts("Copyright (c) 1982, Mike Meyer\n\n") ;
  136.  
  137.     if (argc > 2 || argc < 2) error("usage: tex <file>\n", NULL, 1) ;
  138.  
  139.     for (p = argv[1]; *p && *p != '.'; p++)
  140.         ;
  141.  
  142.     sprintf(filename, *p ? "%s" : "%s.tex", argv[1]) ;
  143.     if (strlen(filename) >= FILENAMELEN)
  144.         error("tex: Invalid file name: %s\n", argv[1], 1) ;
  145.     if (fopen(filename, infile) == ERROR)
  146.         error("tex: Can't open file: %s\n", filename,  1) ;
  147.     *p = NULL ;        /* the output filename WILL be *.ff */
  148.     sprintf(filename, "%s.ff", argv[1]) ;
  149.     if (fcreat(filename, outfile) ==  ERROR)
  150.         error("tex: Can't create file: %s\n", filename, 1) ;
  151.  
  152.     /* initialize the globals... */
  153.     init() ;
  154.  
  155.     inchar = readch(infile) ;
  156.     if (processtext(readch, infile) != EOF)
  157.         printf("tex: Missing right brace in file: %s.TEX\n", argv[1]) ;
  158.     if (mathmode) puts("tex: Document completed in math mode\n") ;
  159.  
  160.     /* cleanup the last pieces of text, and close everything */
  161.     fputs(inbuffer, outfile) ;
  162.     fputs(PARAGRAPH, outfile) ;
  163.     putc(CPMEOF, outfile) ;
  164.     fflush(outfile) ;
  165.     fclose(outfile) ;
  166.     puts("tex: Run complete\n") ;
  167.  
  168.     unlink(FFIFILE) ;
  169.     if (fcreat(FFIFILE, outfile) == ERROR)
  170.         error("tex: Can't create temp file: %s\n", FFIFILE, 1) ;
  171.     /* Possibly not portable, but... */
  172.     if (fprintf(outfile, "%s\n", passtoff) == ERROR
  173.         || fprintf(outfile, "+FO %s %s %s %s %s %s %s %s %s %s ,",
  174.             fontnames[0], fontnames[1], fontnames[2], fontnames[3],
  175.             fontnames[4], fontnames[5], fontnames[6], fontnames[7],
  176.             fontnames[8], fontnames[9]) == ERROR)
  177.         error("tex: Write error on file: %s\n", FFIFILE,1) ;
  178.     putc(CPMEOF, outfile) ;
  179.     fflush(outfile) ;
  180.     fclose(outfile) ;
  181.  
  182.     execl("pfont", filename, "<", FFIFILE, 0) ;
  183.     puts("tex: Can't execute pfont\n") ;
  184.     return ERROR ;
  185.     }
  186. /*
  187.  * init - make the global variables have reasonable values
  188.  */
  189. init() {
  190.     int    i, j ;
  191.  
  192.     puts("tex: Initializing ... ") ;
  193.     fputs(JUSTON, outfile) ;        /* justification defaults on */
  194.     _allocp = NULL ;
  195.     fillmode = TRUE ;
  196.     FFflag = displaymode = mathmode = FALSE ;
  197.     textfont = mathfont = nextdef = cfont = 0 ;
  198.     *passtoff = *(endpointer = inbuffer) = NULL ;
  199.     currentpoint = lastbreakpoint = leftmargin = LEFTDEFAULT ;
  200.     rightmargin = RIGHTDEFAULT ;
  201.     indent = INDENTDEFAULT ;
  202.     for (i = 0; i < NUMFONTS; i++) {
  203.         *(fontnames[i]) = 0 ;
  204.         for (j = 0; j < ASCIICHARS; j++)
  205.             fontdata[i][j] . f_width =
  206.             fontdata[i][j] . f_leftmargin =
  207.             fontdata[i][j] . f_inkwidth =
  208.             fontdata[i][j] . f_inkheight =
  209.             fontdata[i][j] . f_inkbottom =
  210.             DEFAULTCHARLEN ;
  211.         }
  212.     puts("done\n") ;
  213.     }
  214. /*
  215.  * processtext - do the actuall text formatting. This routine consists
  216.  *    of a driver loop that reads characters with the following entry
  217.  *    invariants:
  218.  *        1) currentpoint < rightmargin
  219.  *        2) inchar has the NEXT character to be processed
  220.  *        4) endpointer points to next empty slot in inbuffer
  221.  */
  222. processtext(nextchar, arg) int (*nextchar)(); {
  223.     char    myfont, myfill ;
  224.     int    myleft, myright, myindent ;
  225.  
  226.     for (;; *endpointer = NULL) {
  227.         if (endpointer - inbuffer > INBUF) /* unlikely, but... */
  228.             error("tex: Out of buffer space\n", NULL, 1) ;
  229. #if        TRACEPOINTS
  230.         printf("Char %c, current %d, lastbreak %d, width %d\n",
  231.             inchar, currentpoint, lastbreakpoint, width(inchar)) ;
  232. #endif
  233.         if (!fillmode && inchar == NL) {
  234.                 inchar = SPACE ;    /* will be tossed */
  235.                 makenewline(ERROR) ;
  236.                 inchar = (*nextchar)(arg) ;
  237.                 }
  238.         else if (inchar == SPACE || inchar == TAB || inchar == NL)
  239.             if (!mathmode) dowhite(nextchar, arg) ;
  240.             else inchar = (*nextchar)(arg) ;
  241.         else if (inchar == COMMAND) docommand(nextchar, arg) ;
  242.         else if (inchar == MATHMODE) domath(nextchar, arg) ;
  243.         else if (inchar == COMMENT) skipto(NL, nextchar, arg) ;
  244.         else if (inchar == OPEN) {
  245.             /* stack the environment */
  246.             myfill = fillmode  ;
  247.             myfont = cfont ;
  248.             myleft = leftmargin ;
  249.             myright = rightmargin ;
  250.             myindent = indent ;
  251.  
  252.             /* jump down a level */
  253.             inchar = (*nextchar)(arg) ;
  254.             processtext(nextchar, arg) ;
  255.  
  256.             /* now unstack the environment */
  257.             setfill(myfill) ;
  258.             changetofont(myfont) ;
  259.             leftmargin = myleft ;
  260.             rightmargin = myright ;
  261.             indent = myindent ;
  262.             }
  263.         else if (inchar == CLOSE)
  264.             return inchar = (*nextchar)(arg) ;
  265.         else if (inchar == EOF) return EOF ;
  266.         else if ((currentpoint += width(inchar)) < rightmargin) {
  267.             *endpointer++ = inchar ;
  268.             inchar = (*nextchar)(arg)  ;
  269.             }
  270.         else {
  271.             if (lastbreakpoint == leftmargin) makenewline(ERROR) ;
  272.             else makenewline(currentpoint - lastbreakpoint) ;
  273.             inchar = (*nextchar)(arg) ;
  274.             }
  275.         }
  276.     return OK ;
  277.     }
  278. /*
  279.  * dowhite - handle the word break code...
  280.  */
  281. dowhite(nextchar, arg) int (*nextchar)(arg); {
  282.  
  283.     fputs(inbuffer, outfile) ;        /* flush the current word */
  284.     endpointer = inbuffer ;
  285.  
  286.     do {    /* eat spaces */
  287.         if (inchar == NL) {        /* test for paragraph break */
  288.             if ((inchar = (*nextchar)(arg)) == EOF) break ;
  289.             if (inchar == NL) {    /* DO a paragraph break */
  290.                 fputs(PARAGRAPH, outfile) ;
  291.                 fprintf(outfile, FORMLINE,
  292.                     indent + leftmargin) ;
  293.                 currentpoint = lastbreakpoint
  294.                     = leftmargin + indent ;
  295. #if                TRACEPOINTS
  296.                 puts("currentpoint reset to margin+indent\n") ;
  297. #endif
  298.                 return skipwhite(nextchar, arg) ;
  299.                 }
  300.             }
  301.         else if ((inchar = (*nextchar)(arg)) == EOF) break ;
  302.         if (inchar == COMMENT && skipto(NL, nextchar, arg) == EOF)
  303.             break ;
  304.         } while (inchar == SPACE || inchar == TAB || inchar == NL) ;
  305.  
  306.     if ((currentpoint += width(SPACE)) < rightmargin) {
  307.  
  308.         lastbreakpoint = currentpoint ;
  309.         *endpointer++ = SPACE ;
  310.         }
  311.     else {
  312.         currentpoint = lastbreakpoint = leftmargin ;
  313. #if        TRACEPOINTS
  314.         puts("currentpoint reset to margin\n") ;
  315. #endif
  316.         fputs(ENDLINE, outfile) ;
  317.         fprintf(outfile, FORMLINE, leftmargin) ;
  318.         }
  319.     return inchar ;
  320.     }
  321. /*
  322.  * domath - change the magic math modes around. All this does is
  323.  *    toggles the mathmode/displaymode flags, and outputs the
  324.  *    things to make it change modes. Assumes that the first $
  325.  *    has been read, but not the (possible) second. Leaves inchar
  326.  *    correct for next loop.
  327.  *
  328.  *    The decision table for this toy is as follows:
  329.  *
  330.  *                Mathmode
  331.  *
  332.  *            TRUE        FALSE
  333.  *        TRUE    User Error    Internal Error        inchar != $
  334.  *        FALSE    MM=F        MM=T
  335.  * Displaymode
  336.  *        TRUE    MM=DM=F        Internal Error        inchar == $
  337.  *        FALSE    User Error    MM=DM=T
  338.  *
  339.  *    N.B. - this code should probably be rewritten as an else-if chain,
  340.  *    as it isn't used very often; We could afford the loss in speed for
  341.  *    the gain in size. Hence this is a good place to trim.
  342.  */
  343. domath(nextchar, arg) int (*nextchar)(); {
  344.  
  345.     inchar = (*nextchar)(arg) ;
  346.     fputs(inbuffer, outfile) ;
  347.     endpointer = inbuffer ;
  348.  
  349.     switch ((mathmode ? 1 : 0)
  350.        + (displaymode ? 2 : 0)
  351.        + (inchar == MATHMODE ? 4 : 0)) {    
  352.  
  353.         case 0: /* !mathmode, !displaymode, inchar != $ */
  354.             mathmode = TRUE ;
  355.             fputs(ONMATH, outfile) ;
  356.             textfont = cfont ;
  357.             changetofont(mathfont) ;
  358.             return inchar ;
  359.  
  360.         case 1: /* mathmode, !displaymode, inchar != $ */
  361.             mathmode = FALSE ;
  362.             fputs(OFFMATH, outfile) ;
  363.             changetofont(textfont) ;
  364.             return inchar ;
  365.  
  366.         case 2: /* !mathmode, displaymode, inchar != $ */
  367.         case 6:    /* !mathmode, displaymode, inchar == $ */
  368.             error("tex: Internal error in mathmode: inchar = %c\n",
  369.                 inchar, 1) ;
  370.  
  371.         case 3: /* mathmode, displaymode, inchar != $ */
  372.             puts("tex: Leaving display mode with `$'\n") ;
  373.             mathmode = displaymode = FALSE ;
  374.             changetofont(textfont) ;
  375.             fputs(OFFDISPLAY, outfile) ;
  376.             fprintf(outfile, FORMLINE, leftmargin) ;
  377.             lastbreakpoint = currentpoint = leftmargin ;
  378.             return skipwhite(nextchar, arg) ;
  379.  
  380.         case 4: /* !mathmode, !displaymode, inchar == $ */
  381.             mathmode = displaymode = TRUE ;
  382.             fputs(ONDISPLAY, outfile) ;
  383.             textfont = cfont ;
  384.             changetofont(mathfont) ;
  385.             lastbreakpoint = currentpoint = leftmargin ;
  386.             return inchar = (*nextchar)(arg) ;
  387.  
  388.         case 5: /* mathmode, !displaymode, inchar == $ */
  389.             puts("tex: Leaving math mode with `$$'\n") ;
  390.             mathmode = displaymode = FALSE ;
  391.             fputs(OFFMATH, outfile) ;
  392.             changetofon(textfont) ;
  393.             return inchar = (*nextchar)(arg) ;
  394.  
  395.         case 7: /* mathmode, displaymode, inchar == $ */
  396.             mathmode = displaymode = FALSE ;
  397.             changetofont(textfont) ;
  398.             fputs(OFFDISPLAY, outfile) ;
  399.             fprintf(outfile, FORMLINE, leftmargin) ;
  400.             lastbreakpoint = currentpoint = leftmargin ;
  401.             inchar = (*nextchar)(arg) ;
  402.             return skipwhite(nextchar, arg) ;
  403.  
  404.         default: /* I can't get here from there! */
  405.             error("tex: Domath compiler error!\n", NULL, 1) ;
  406.         }
  407.     }
  408. /*
  409.  * docommand - take care of command strings in the text of the
  410.  *    program.  This is generally handled by dumping magic
  411.  *    straight to outfile, or invoking processtext (recursively)
  412.  *    on some other intput stream. Note that this assumes that inchar
  413.  *    has the COMMAND char in it, and that it leaves inchar updated...
  414.  */
  415. docommand(nextchar, arg) int (*nextchar)(); {
  416.     char    *compointer, command[COMLENGTH] ;
  417.  
  418.     if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  419.     *(compointer = command) = inchar ;
  420.  
  421.     if (!isalpha(inchar)) {
  422.         command[1] = NULL ;
  423.         inchar = (*nextchar)(arg) ;
  424.         }
  425.     else {
  426.         /*
  427.          * inchar HAS to be in this loop, to prevent EOF's from
  428.          *    getting clipped to DEL's...
  429.          */
  430.         while (isalpha(*++compointer = inchar = (*nextchar)(arg)))
  431.             if (compointer - command >= COMLENGTH - 1) {
  432.                 *compointer = NULL ;
  433.                 printf("tex: Command too long: %s\n", command);
  434.                 return skiptowhite(nextchar, arg) ;
  435.                 }
  436.         *compointer = NULL ;
  437.         texcase(command) ;
  438.         }
  439.     skipwhite(nextchar, arg) ;
  440.  
  441.     if (strequal(command, "f")) return dofont(nextchar, arg) ;
  442.     if (strequal(command, "char")) return dochar(nextchar, arg) ;
  443.     if (strequal(command, "mathonly")) return checkmath(nextchar, arg) ;
  444.     if (strequal(command, "def")) return dodef(nextchar, arg) ;
  445.     if (strequal(command, "eject")) return doeject(nextchar, arg) ;
  446.     if (strequal(command, "ctr")) return docenter(nextchar, arg) ;
  447.     if (strequal(command, "rt")) return doright(nextchar, arg) ;
  448.     if (strequal(command, "lft")) return doleft(nextchar, arg) ;
  449.     if (strequal(command, "underline")) return dounder(nextchar, arg) ;
  450.     if (strequal(command, "fill")) return dofill(nextchar, arg) ;
  451.     if (strequal(command, "vskip")) return dovert(nextchar, arg) ;
  452.     if (strequal(command, "hskip")) return dohoriz(nextchar, arg) ;
  453.     if (strequal(command, "par")) return dopar(nextchar, arg) ;
  454.     if (strequal(command, "indent")) return setindent(nextchar, arg) ;
  455.     if (strequal(command, "lftmarg")) return setleft(nextchar, arg) ;
  456. #if    RIGHT
  457.     if (strequal(command, "rtmarg")) return setright(nextchar, arg) ;
  458. #endif
  459.     if (strequal(command, "input")) return doinput(nextchar, arg) ;
  460.     if (strequal(command, "math")) return setmath(nextchar, arg) ;
  461.     if (strequal(command, "ff")) return doff(nextchar, arg) ;
  462.     if (lookup(command, nextchar, arg)) return inchar ;
  463.     printf("tex: Illegal command: \\%s\n", command) ;
  464.     return inchar ;
  465.     }
  466. /*
  467.  * dofont - take care of font changes/load. Assumes that the font char hasn't
  468.  *    been gotten, but that the ':' in the font command has. Will return
  469.  *    EOF/not EOF
  470.  */
  471. dofont(nextchar, arg) int (*nextchar)(); {
  472.     char    newfont ;
  473.  
  474.     if (inchar != FONTCHAR) {
  475.         printf("tex: Illegal font command: \\f%c\n", inchar) ;
  476.         return inchar = (*nextchar)(arg) ;
  477.         }
  478.     inchar = (*nextchar)(arg) ;
  479.     if (inchar < '0' || inchar > '9') {
  480.         printf("tex: Illegal font character: %c\n", newfont) ;
  481.         if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  482.         if (inchar != '=') return inchar ;
  483.         /* eat up the file name... */
  484.         if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  485.         if (skipwhite(nextchar, arg) == EOF
  486.            || skiptowhite(nextchar, arg) == EOF) return EOF ;
  487.         return skipwhite(nextchar, arg) ;
  488.         }
  489.     newfont = inchar - '0' ;
  490.     if ((inchar = (*nextchar)(arg)) == '=')
  491.         return loadfont(nextchar, arg, newfont) ;
  492.     changetofont(newfont) ;
  493.     return skipwhite(nextchar, arg) ;
  494.     }
  495. /*
  496.  * loadfont - load a font data file into memory. This thing opens the
  497.  *    data file from Softcraft, load the right font file, and then goes
  498.  *    on it's merry way. Note that it doesn't [I repeat - DOES NOT!]
  499.  *    reinitialize the font tables, so loading a second font on top
  500.  *    of a used font character can produce unpredictable results.
  501.  *    This does leave inchar pointed at the next character, and returns
  502.  *    EOF/OK.
  503.  */
  504. loadfont(nextchar, arg, newfont) int (*nextchar)(); {
  505.     char    i, c, fontfile[BUFSIZ], filename[FILENAMELEN] ;
  506.     struct chardata    *pointer ;
  507.  
  508.     if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  509.     getfilename(filename, nextchar, arg) ;
  510.     strcpy(fontnames[newfont], filename) ;
  511.     strcat(filename, ".fon") ;
  512.     if (fopen(filename, fontfile) == ERROR) {
  513.         printf("tex: Can't open font file: %s\n", filename) ;
  514.         return OK ;
  515.         }
  516.  
  517.     printf("tex: Loading font file %s ... ", filename) ;
  518.     for (i = 0; i++ < 42;)
  519.         getc(fontfile) ;        /* chew up header */
  520.     while (c = getc(fontfile))
  521.         ;                /* look for trailing null */
  522.     if (getc(fontfile) != 2 || getc(fontfile) != 3) {
  523.         printf("tex: Bad font file: %s\n", filename) ;
  524.         fclose(fontfile) ;
  525.         return OK ;
  526.         }
  527.     getc(fontfile); getc(fontfile) ;    /* eat more stuff */
  528.     i = getc(fontfile);            /* # of defined characters */
  529. /* DEBUG - you really need to check to see if we ran out of file on these
  530. getc's.. */
  531.  
  532.     for (c = 6; c++ <= 15;)
  533.         getc(fontfile) ;        /* eat reserved bytes */
  534.  
  535.     while (i--) {                /* loop over defined bytes */
  536.         c = getc(fontfile) ;        /* the character defined */
  537.         if (c >= ASCIICHARS || c < 0) {    /* signed or unsigned chars? */
  538.             printf("tex: Bad font file: %s\n", filename) ;
  539.             return OK ;
  540.             }
  541.         pointer = &fontdata[newfont][c] ;
  542.         /* now fetch the data that goes with the character */
  543.         pointer -> f_width = getc(fontfile) + 2 ;
  544.         pointer -> f_leftmargin = getc(fontfile) ;
  545.         pointer -> f_inkwidth = getc(fontfile) ;
  546.         pointer -> f_inkheight = getc(fontfile) ;
  547.         pointer -> f_inkbottom  = getc(fontfile) ;
  548.         }
  549.     puts("done\n") ;
  550.  
  551.     fclose(fontfile) ;        /* clean up after myself */
  552.     return OK ;
  553.     }
  554. /*
  555.  * dochar - do the 'char' command. Leaves inchar in the right state for the
  556.  *    next loop of processtext.
  557.  */
  558. dochar(nextchar, arg) int (*nextchar)(); {
  559.     int    tempin ;
  560.     char    tempbuf[FILENAMELEN] ;
  561.  
  562.     getnumber(&tempin, nextchar, arg) ;
  563.     if (tempin < 0 || tempin > 127) {
  564.         printf("tex: Invalid character: %03d\n", tempin) ;
  565.         return inchar ;
  566.         }
  567. #if    TRACEPOINTS
  568.     printf("currentpoint %d, lastbreak %d, width %d\n",
  569.         currentpoint, lastbreakpoint, width(tempin)) ;
  570. #endif
  571.     currentpoint += width(tempin) ;
  572.     /* Curse you, DRI! */
  573.     if (!tempin || tempin == CPMEOF || tempin == COMMAND)
  574.         sprintf(tempbuf, FORMCHAR, tempin) ;
  575.     else sprintf(tempbuf, "%c", tempin) ;
  576.     forceout(tempbuf) ;
  577.     return inchar ;
  578.     }
  579. /*
  580.  * checkmath - just check to see if we are in math mode, and bitch
  581.  *    if not...
  582.  */
  583. checkmath() {
  584.  
  585.     if (!mathmode)
  586.         puts("tex: Math only command issued outside math mode\n") ;
  587.     return OK ;
  588.     }
  589. /*
  590.  * doinput - change to a different input stream. This routine
  591.  *    works by running a second copy of processtext with a change
  592.  *    of input stream. Leaves inchar in the right state.
  593.  */
  594. doinput(nextchar, arg) int (*nextchar)(); {
  595.     char    *p, filename[FILENAMELEN], filebuf[BUFSIZ] ;
  596.     int    savechar ;
  597.  
  598.     getfilename(filename, nextchar, arg) ;
  599.     if (strlen(filename) > FILENAMELEN - 5) {
  600.         printf("tex: Invalid file name: %s\n", filename) ;
  601.         return inchar ;
  602.         }
  603.     for (p = filename; *p;)
  604.         if (*p++ == '.') {
  605.             printf("tex: Invalid file name: %s\n", filename) ;
  606.             return inchar ;
  607.             }
  608.     strcat(filename, ".tex") ;
  609.     if (fopen(filename, filebuf) == ERROR) {
  610.         printf("tex: Can't open file: %s\n", filename) ;
  611.         return inchar ;
  612.         }
  613.     savechar = inchar ;
  614.     inchar = readch(filebuf) ;
  615.  
  616.     printf("tex: Processing file %s\n", filename) ;
  617.     if (processtext(readch, filebuf) != EOF)
  618.         printf("tex: Missing right brace in file: %s\n", filename) ;
  619.     printf("tex: Finished with file %s\n", filename) ;
  620.  
  621.     return inchar = savechar ;
  622.     }
  623. /*
  624.  * dodef - do the define command. This expects a '\' in inchar, and
  625.  *    eats up a define command afterwards. The syntax for the
  626.  *    (currently) accepted define commands are:
  627.  *
  628.  *    \def\defname#1{text of define}    \def\x#1{text of define}
  629.  *    \def\defname{text of define}    \def\x{text of define}
  630.  *
  631.  *    The string #1 following the defname indicates a parameter. The
  632.  *    x in the right-hand defines is a single non-alphabetic character.
  633.  */
  634. dodef(nextchar, arg) int (*nextchar)(); {
  635.     char    *defpointer, defname[COMLENGTH] ;
  636.  
  637.     if (inchar != COMMAND) {
  638.         printf("tex: Bad define character. Expected %c, got %c\n",
  639.                             COMMAND, inchar) ;
  640.         return inchar = (*nextchar)(arg) ;
  641.         }
  642.  
  643.     if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  644.     *(defpointer = defname) = inchar ;
  645.  
  646.     if (!isalpha(inchar)) {
  647.         defname[1] = NULL ;
  648.         inchar = (*nextchar)(arg) ;
  649.         }
  650.     else {
  651.         while ((inchar = (*nextchar)(arg)) != OPEN
  652.             && inchar != PARAMETER) {
  653.             if (defpointer - defname >= COMLENGTH - 1) {
  654.                 *defpointer = NULL ;
  655.                 printf("tex: Define to long: %s\n", defname) ;
  656.                 return skiptowhite(nextchar, arg) ;
  657.                 }
  658.             *++defpointer = inchar ;
  659.             }
  660.         *++defpointer = NULL ;
  661.         texcase(defname) ;
  662.         }
  663.  
  664.     if (inchar == OPEN) return definsert(defname, nextchar, arg, FALSE) ;
  665.     if (inchar != PARAMETER) {
  666.         puts("tex: Bad define: %s", defname) ;
  667.         return skiptowhite(nextchar, arg) ;
  668.         }
  669.     if ((inchar = (*nextchar)(arg)) != '1') {
  670.         printf("tex: Bad define parameter. Expected 1, got %c", 
  671.             inchar) ;
  672.         return OK ;
  673.         }
  674.     if ((inchar = (*nextchar)(arg)) == OPEN)
  675.         return definsert(defname, nextchar, arg, TRUE) ;
  676.     printf("tex: Bad define: %s", defname) ;
  677.     return skiptowhite(nextchar, arg) ;
  678.     return OK ;
  679.     }
  680. /*
  681.  * definsert - put the named object in the define table. The parameter
  682.  *    flag is copied appropriately.
  683.  *
  684.  *    Note: right now, the parameter flag could be set in dodef, but
  685.  *    if I change the table search (binary search, or hashing, or
  686.  *    whatever), this would no longer be true.
  687.  */
  688. definsert(name, nextchar, arg, parameter) int (*nextchar)(); char *name; {
  689.     char    token[INBUF] ;
  690.  
  691.     if (nextdef >= NUMDEFS)
  692.         error("tex: To many defines: %s not defined\n", name, 1) ;
  693.     defines[nextdef] . d_parameter = parameter ;
  694.     strcpy(defines[nextdef] . d_defname, name) ;
  695.     gettoken(token, INBUF, nextchar, arg) ;
  696.     if ((defines[nextdef] . d_replace = sbrk(strlen(token) + 2)) == ERROR)
  697.         error("tex: Out of string space\n") ;
  698.     strcpy(defines[nextdef++] . d_replace, token) ;
  699.     return inchar ;
  700.     }
  701. /*
  702.  * lookup - decide if command is in the define tables. If it is, feed
  703.  *    it to processtext and return TRUE, otherwise return FALSE.
  704.  *    In either case, make sure that inchar is correct for the future.
  705.  *    If no input is done, this is already true...
  706.  */
  707. lookup(command, nextchar, arg) char *command; int (*nextchar)(); {
  708.     int    i, savechar ;
  709.     char    token[INBUF] ;
  710.  
  711.     for (i = 0;; i++)
  712.         if (i >= nextdef) return FALSE ;
  713.         else if (strequal(command, defines[i] . d_defname)) break ;
  714.  
  715.     defines[i] . d_nextchar = defines[i] . d_replace ;
  716.     if (!defines[i] . d_parameter) {
  717.         savechar = inchar ;
  718.         inchar = stringch(&defines[i] . d_nextchar) ;
  719.         processtext(stringch, &defines[i] . d_nextchar) ;
  720.         }
  721.     else {
  722.         gettoken(token, INBUF, nextchar, arg) ;
  723.         savechar = inchar ;
  724.         defines[i] . d_altparm = NULL ;
  725.         defines[i] . d_parameter = token ;
  726.         inchar = defch(defines + i) ;
  727.         processtext(defch, defines + i) ;
  728.         }
  729.     inchar = savechar ;
  730.     return TRUE ;
  731.     }
  732. /*
  733.  * doeject - force a new page to start.
  734.  */
  735. doeject(nextchar, arg) int (*nextchar)(); {
  736.  
  737.     lastbreakpoint = currentpoint = leftmargin ;
  738.     forceout(PAGE) ;
  739.     return skipwhite(nextchar, arg) ;
  740.     }
  741. /*
  742.  * docenter - Center the following token on the line, doing the
  743.  *    appropriate magic with inchar.
  744.  */
  745. docenter(nextchar, arg) int (*nextchar)(); {
  746.     char    buffer[INBUF], *charpointer ;
  747.     int    savechar ;
  748.  
  749.     gettoken(charpointer = buffer, INBUF, nextchar, arg) ;
  750.     forceout(CENTER) ;
  751.     savechar = inchar ;
  752.     inchar = stringch(&charpointer) ;
  753.     processtext(stringch, &charpointer) ;
  754.     return inchar = savechar ;
  755.     }
  756. /*
  757.  * doleft - The following code should be on the left of the line, but...
  758.  *    do the appropriate magic with inchar.
  759.  */
  760. doleft(nextchar, arg) int (*nextchar)(); {
  761.     char    buffer[INBUF], *charpointer ;
  762.     int    savechar ;
  763.  
  764.     gettoken(charpointer = buffer, INBUF, nextchar, arg) ;
  765.     forceout(LEFTIFY) ;
  766.     savechar = inchar ;
  767.     inchar = stringch(&charpointer) ;
  768.     processtext(stringch, &charpointer) ;
  769.     return inchar = savechar ;
  770.     }
  771. /*
  772.  * doright - Put the following text on the far right side of the line, doing
  773.  *    the appropriate magic with inchar.
  774.  */
  775. doright(nextchar, arg) int (*nextchar)(); {
  776.     char    buffer[INBUF], *charpointer ;
  777.     int    savechar ;
  778.  
  779.     gettoken(charpointer = buffer, INBUF, nextchar, arg) ;
  780.     forceout(RIGHTIFY) ;
  781.     savechar = inchar ;
  782.     inchar = stringch(&charpointer) ;
  783.     processtext(stringch, &charpointer) ;
  784.     lastbreakpoint = currentpoint = leftmargin ;
  785.     makenewline(ERROR) ;
  786.     return inchar = savechar ;
  787.     }
  788. /*
  789.  * dounder - turn on the underline mode for a token or so. Does the
  790.  *    appropriate magic with inchar.
  791.  */
  792. dounder(nextchar, arg) int (*nextchar)(); {
  793.     char    buffer[INBUF], *charpointer ;
  794.     int    savechar ;
  795.  
  796.     gettoken(charpointer = buffer, INBUF, nextchar, arg) ;
  797.     forceout(UNDERLINE) ;
  798.     savechar = inchar ;
  799.     inchar = stringch(&charpointer) ;
  800.     processtext(stringch, &charpointer) ;
  801.     forceout(UNDERLINE) ;
  802.     return inchar = savechar ;
  803.     }
  804. /*
  805.  * dofill - reads in the next number. If it is zero, turn off word filling,
  806.  *    otherwise turn on word filling. Does the right thing with inchar.
  807.  */
  808. dofill(nextchar, arg) int (*nextchar)(); {
  809.     char    flag, buffer[MAXLINE] ;
  810.  
  811.     getnumber(&flag, nextchar, arg) ;
  812.     setfill(flag != 0) ;
  813.     return inchar ;
  814.     }
  815. /*
  816.  * setmath - does the \math command to set the mathmode variable to
  817.  *    the rigth thing. Assumes that inchar has the : and that a
  818.  *    digit follows thereafter. Makes inchar right.
  819.  */
  820. setmath(nextchar, arg) int (*nextchar)(arg); {
  821.  
  822.     if (inchar != FONTCHAR) {
  823.         puts("tex: Illegal command \\math%c\n", inchar) ;
  824.         return inchar = (*nextchar)(arg) ;
  825.         }
  826.     if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  827.  
  828.     if (inchar < '0' || inchar > '9') {
  829.         printf("tex: Illegal font character: %c\n", inchar) ;
  830.         return inchar = (*nextchar)(arg) ;
  831.         }
  832.     mathfont = inchar - '0' ;
  833.  
  834.     if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  835.     return skipwhite(nextchar, arg) ;
  836.     }
  837. /*
  838.  * dopar - force a paragraph to start/end...
  839.  */
  840. dopar(nextchar, arg) int (*nextchar)(); {
  841.     char    buffer[MAXLINE] ;
  842.  
  843.     currentpoint = lastbreakpoint = leftmargin + indent  ;
  844.     forceout(PARAGRAPH) ;
  845.     sprintf(buffer, FORMLINE, currentpoint) ;
  846.     forceout(buffer) ;
  847.     return OK ;
  848.     }
  849. /*
  850.  * dohoriz - skip over some horizontal number of points...
  851.  */
  852. dohoriz(nextchar, arg) int (*nextchar)(); {
  853.     int    number ;
  854.     char    buffer[MAXLINE] ;
  855.  
  856.     getnumber(&number, nextchar, arg) ;
  857.     if (inchar != 'p' || (inchar = (*nextchar)(arg)) != 't')
  858.         puts("tex: Need `pt' to follow hskip number\n") ;
  859.     inchar = (*nextchar)(arg) ;    /* skip over the t... */
  860.  
  861.     currentpoint += number ;
  862.     sprintf(buffer, FORMHORIZ, number) ;
  863.     forceout(buffer) ;
  864.     return skipwhite(nextchar, arg) ;
  865.     }
  866. /*
  867.  * dovert - skip over some vertical number of points...
  868.  */
  869. dovert(nextchar, arg) int (*nextchar)(); {
  870.     int    number ;
  871.     char    buffer[MAXLINE] ;
  872.  
  873.     getnumber(&number, nextchar, arg) ;
  874.     if (inchar != 'p' || (inchar = (*nextchar)(arg)) != 't')
  875.         puts("tex: Need `pt' to follow vskip number\n") ;
  876.     inchar = (*nextchar)(arg) ;    /* skip over the t... */
  877.  
  878.     currentpoint = lastbreakpoint = leftmargin ;
  879.     sprintf(buffer, FORMVERT, number) ;
  880.     forceout(buffer) ;
  881.     return skipwhite(nextchar, arg) ;
  882.     }
  883. /*
  884.  * setleft - set the left margin to some new value. Leaves inchar updated.
  885.  */
  886. setleft(nextchar, arg) int (*nextchar)(); {
  887.     int    number, flag ;
  888.  
  889.     if ((flag = getnumber(&number, nextchar, arg)) == 0)
  890.         leftmargin = number ;
  891.     else if (flag > 0) leftmargin += number ;
  892.     else if (flag < 0) leftmargin -= number ;
  893.     if (leftmargin < 0) {
  894.         puts("tex: Negative left margin, left set to 0\n") ;
  895.         leftmargin = 0 ;
  896.         }
  897.     return inchar ;
  898.     }
  899. #if    RIGHT
  900. /*
  901.  * setright - like setleft, but for the right margin.
  902.  */
  903. setright(nextchar, arg) int (*nextchar)(); {
  904.     int    number, flag ;
  905.  
  906.     if ((flag = getnumber(&number, nextchar, arg)) == 0)
  907.         rightmargin = number ;
  908.     else if (flag > 0) rightmargin += number ;
  909.     else if (flag < 0) rightmargin -= number ;
  910.     return inchar ;
  911.     }
  912. #endif
  913. /*
  914.  * setindent - setleft/setright again, but for the indentation.
  915.  */
  916. setindent(nextchar, arg) int (*nextchar)(); {
  917.     int    number, flag ;
  918.  
  919.     if ((flag = getnumber(&number, nextchar, arg)) == 0)
  920.         indent = number ;
  921.     else if (flag > 0) indent += number ;
  922.     else if (flag < 0) indent -= number ;
  923.     if (leftmargin + indent < 0) {
  924.         printf("tex: Indent to small, set to: %d", -leftmargin) ;
  925.         indent = -leftmargin ;
  926.         }
  927.     return inchar ;
  928.     }
  929. /*
  930.  * doff - get things from the input stream to be passed to Fancy Fonts.
  931.  *    This also leaves inchar done up right.
  932.  */
  933. doff(nextchar, arg) int (*nextchar)(); {
  934.  
  935.     gettoken(passtoff, INBUF, nextchar, arg) ;
  936.     return inchar ;
  937.     }
  938. /*
  939.  * getfilename - go fetch a file name from the current input stream.
  940.  *    reads the name into name, and leaves inchar pointed to the
  941.  *    right thing.
  942.  */
  943. getfilename(name, nextchar, arg) char *name; int (*nextchar)(); {
  944.     char    counter ;
  945.  
  946.     if (skipwhite(nextchar, arg) == EOF) return EOF ;
  947.     for (counter = 0; inchar != EOF; inchar = (*nextchar)(arg)) {
  948.         if (counter > FILENAMELEN - 5) {
  949.             strcpy(name, "bad name") ;
  950.             if (skiptowhite(nextchar, arg) == EOF)
  951.                 return EOF ;
  952.             return skipwhite(nextchar, arg) ;
  953.             }
  954.         if (isalpha(inchar) || isdigit(inchar) || inchar == ':')
  955.             *name++ = inchar ;
  956.         else {            /* got the whole thing */
  957.             *name = NULL ;
  958.             return skipwhite(nextchar, arg) ;
  959.             }
  960.         }
  961.     *name = NULL ;            /* EOF - make sure to turn it off */
  962.     return EOF ;
  963.     }
  964. /*
  965.  * getnumber - fetch a number from the input stream, skipping whitespace
  966.  *    afterwards. Automagically leaves inchar right.
  967.  */
  968. getnumber(number, nextchar, arg) int *number, (*nextchar)(arg); {
  969.     int    sign ;
  970.  
  971.     sign = *number = 0 ;
  972.     if (skipwhite(nextchar, arg) == EOF) return 0 ;
  973.     if (inchar == '+') sign = 1 ;
  974.     else if (inchar == '-') sign = -1 ;
  975.  
  976.     if (sign) inchar = (*nextchar)(arg) ;
  977.     while (isdigit(inchar)) {
  978.         *number = 10 * *number + (inchar - '0') ;
  979.         if ((inchar = (*nextchar)(arg)) == EOF) return sign ;
  980.         }
  981.     skipwhite(nextchar, arg) ;
  982.     return sign ;
  983.     }
  984. /*
  985.  * gettoken - fetch a token out of the input stream. A token is defined
  986.  *    as a single character, OR a string surrounded by OPEN and CLOSE.
  987.  *    Expects inchar to have an OPEN (if there is one), and leaves it
  988.  *    updated.
  989.  */
  990. gettoken(token, length, nextchar, arg) int (*nextchar)(); char *token; {
  991.     char    count ;
  992.  
  993.     if (inchar != OPEN) {
  994.         *token++ = inchar ;
  995.         *token = NULL ;
  996.         inchar = (*nextchar)(arg) ;
  997.         return OK ;
  998.         }
  999.  
  1000.     count = 0 ;
  1001.     while ((inchar = (*nextchar)(arg)) != CLOSE || count > 0) {
  1002.         if (inchar == EOF) {
  1003.             *token = NULL ;
  1004.             return ERROR ;
  1005.             }
  1006.         if (length > 0) {
  1007.             *token++ = inchar ;
  1008.             length-- ;
  1009.             }
  1010.         if (inchar == OPEN) count++ ;
  1011.         if (inchar == CLOSE) count-- ;
  1012.         }
  1013.     *token = NULL ;
  1014.     inchar = (*nextchar)(arg) ;
  1015.     skipwhite(nextchar, arg) ;
  1016.     if (length <= 0) puts("tex: Input token to long\n") ;
  1017.     return OK;
  1018.     }
  1019. /*
  1020.  * width - return the width of the argument character in the current font
  1021.  */
  1022. char
  1023. width(ch) char ch; {
  1024.  
  1025.     return fontdata[cfont][ch] . f_width;
  1026.     }
  1027. /*
  1028.  * skipwhite - skips over whitespace in the input stream. Assumes that inchar
  1029.  *    has the next char to be processed, and it may/may not need to be
  1030.  *    skipped.
  1031.  */
  1032. skipwhite(nextchar, arg) int (*nextchar)(); {
  1033.  
  1034.     while (inchar == SPACE || inchar == TAB || inchar == NL) {
  1035.         if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  1036.         if (inchar == COMMENT) skipto(NL, nextchar, arg) ;
  1037.         }
  1038.     return inchar ;
  1039.     }
  1040. /*
  1041.  * skiptowhite - skips over non-white things. Generally used for eating
  1042.  *    bad commands and the like. Same assumptions as skipwhite
  1043.  */
  1044. skiptowhite(nextchar, arg) int (*nextchar)(); {
  1045.  
  1046.     while (inchar != SPACE && inchar != TAB && inchar != NL
  1047.         && inchar != EOF) {
  1048.         if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  1049.         if (inchar = COMMENT) skipto(NL, nextchar, arg) ;
  1050.         }
  1051.     return inchar ;
  1052.     }
  1053. /*
  1054.  * skipto - skip to the character asked for.
  1055.  */
  1056. skipto(ch, nextchar, arg) char ch; int (*nextchar)(arg); {
  1057.  
  1058.     while (inchar != ch)
  1059.         if ((inchar = (*nextchar)(arg)) == EOF) return EOF ;
  1060.     }
  1061. /*
  1062.  * setfill - set the fill mode to the  passed flag...
  1063.  */
  1064. setfill(flag) char flag; {
  1065.  
  1066.     if (fillmode == flag) return OK ;
  1067.     fillmode = flag  ;            /* change fill & */
  1068.     /* now set the justification properly */
  1069.     forceout(fillmode ? JUSTON : JUSTOFF, outfile) ;
  1070.     
  1071.     }
  1072. /*
  1073.  * changetofont - make the current font be the font passed as an argument
  1074.  */
  1075. changetofont(newfont) char newfont; {
  1076.     char    strtemp[5] ;
  1077.  
  1078.     if (newfont == cfont) return OK ;    /* right font, don't change */
  1079.     if (newfont < 0 || newfont > 9) {
  1080.         printf("tex: Illegal font character: %d\n", newfont) ;
  1081.         return OK ;
  1082.         }
  1083.     cfont = newfont ;            /* do the font change */
  1084.     sprintf(strtemp, FORMFONT, newfont) ;
  1085.     forceout(strtemp) ;
  1086.     }
  1087. /*
  1088.  * texcase - change the case of the passed string to lower,
  1089.  *    except for leaving the first character in the case it is in...
  1090.  */
  1091. texcase(string) char *string; {
  1092.  
  1093.     if (!*string)
  1094.         error("tex: Internal error in texcase.\n", NULL, 1) ;
  1095.     while (*++string)
  1096.         *string = tolower(*string) ;
  1097.     }
  1098. /*
  1099.  * forceout - puts the string in the output stream, RIGHT NOW!
  1100.  */
  1101. forceout(string) char *string; {
  1102.  
  1103.     while (*string)
  1104.         *endpointer++ = *string++ ;
  1105.     *endpointer = NULL ;
  1106.     }
  1107. /*
  1108.  * defch - traipse through the characters in a define, possibly with
  1109.  *    parameters.
  1110.  */
  1111. defch(string) struct defdata *string; {
  1112.     char    ch ;
  1113.  
  1114.     if (string -> d_altparm) {
  1115.         if (ch = *(string -> d_altparm++)) return ch ;
  1116.         string -> d_altparm = NULL ;
  1117.         }
  1118.  
  1119.     if (!(ch = *(string -> d_nextchar++))) return EOF ;
  1120.     if (!string -> d_parameter || ch != PARAMETER) return ch ;
  1121.  
  1122.     if ((ch = *(string -> d_nextchar++)) == PARAMETER) return PARAMETER ;
  1123.     if (ch != '1') {
  1124.         printf("tex: Bad parameter number %c\n", ch) ;
  1125.         return EOF ;
  1126.         }
  1127.     string -> d_altparm = string -> d_parameter ;
  1128.     return defch(string) ;
  1129.     }
  1130. /*
  1131.  * stringch - go through the characters in a string, one at a time.
  1132.  */
  1133. stringch(string) char **string; {
  1134.     char    ch ;
  1135.  
  1136.     if (!(ch = *(*string)++)) return EOF ;
  1137.     return ch ;
  1138.     }
  1139. /*
  1140.  * readch - a `censored' read character. Throws out carriage returns and checks
  1141.  *    for CPMEOF.
  1142.  */
  1143. readch(file) char *file; {
  1144.     int    c ;
  1145.  
  1146.     while ((c = getc(file)) == CR)
  1147.         ;
  1148.     if (c == CPMEOF) return EOF ;
  1149.     return c ;
  1150.     }
  1151. /*
  1152.  * makenewline - start us a fresh new line, complete with FF \a command,
  1153.  *    etc. If offset is zero, it assumes that the buffer should be flushed.
  1154.  *    Otherwise, it assumes that what's in the buffer is offset long, and
  1155.  *    should be left in the buffer to start the next line.
  1156.  */
  1157. makenewline(offset) {
  1158.  
  1159.     if (offset == ERROR) {
  1160.         fputs(inbuffer, outfile) ;
  1161.         endpointer = inbuffer ;
  1162.         }
  1163.     fputs(ENDLINE, outfile) ;
  1164.     fprintf(outfile, FORMLINE, leftmargin) ;
  1165.  
  1166.     *endpointer++ = inchar ;
  1167.     currentpoint = leftmargin + width(inchar)
  1168.         + (offset == ERROR ? 0 : offset) ;
  1169.     lastbreakpoint = leftmargin ;
  1170.     if (*inbuffer == SPACE) {
  1171.         currentpoint -= width(SPACE) ;
  1172.         *endpointer = NULL ;
  1173.         strcpy(inbuffer, inbuffer + 1) ;
  1174.         endpointer-- ;
  1175.         }
  1176. #if    TRACEPOINTS
  1177.     puts("currentpoint reset to margin + offset (maybe)\n") ;
  1178. #endif
  1179.     }
  1180. /*
  1181.  * error - print a message (possibly with an argument), and exit
  1182.  */
  1183. error(format, arg, code) char *format; {
  1184.  
  1185.     printf(format, arg) ;
  1186.     exit(code) ;
  1187.     }
  1188. /*
  1189.  * topofstack - debuging routine tells me where the top of the stack is
  1190.  */
  1191. topofstack(x) {
  1192.  
  1193.     return &x ;
  1194.     }
  1195.  it assumes that the buffer should be flushed.
  1196.  *    Otherwise, it assu